﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SegGen
{
   public class Temporary_Population:Population
    {

       // the list of top individuals
      //  List<Individual> m_topIndList = new List<Individual>();

        //public List<Individual> TopIndList
        //{
        //    get { return m_topIndList; }
        //    set { m_topIndList = value; }
        //}

        private int m_numTopIndividuals;

        public int NumTopIndividuals
        {
            get { return m_numTopIndividuals; }
            set { m_numTopIndividuals = value; }
        }

        private double m_crossoverFraction;

        public double CrossoverFraction
        {
            get { return m_crossoverFraction; }
            set { m_crossoverFraction = value; }
        }
        private double m_mutationFraction;

        public double MutationFraction
        {
            get { return m_mutationFraction; }
            set { m_mutationFraction = value; }
        }
        private double m_reproductionFraction;

        public double ReproductionFraction
        {
            get { return m_reproductionFraction; }
            set { m_reproductionFraction = value; }
        }


        private Temporary_Population()
            : base(0)
        {
            m_crossoverFraction = 0;
            m_mutationFraction = 0;
            m_reproductionFraction = 0;
        }

        public Temporary_Population(double _crossoverFraction, double _mutationFraction,
            double _reproductionFraction, int _numTopIndividuals, int numNucleotidesInCodon)
            : base(numNucleotidesInCodon)
        {
            m_crossoverFraction = _crossoverFraction;
            m_mutationFraction = _mutationFraction;
            m_reproductionFraction = _reproductionFraction;
            m_numTopIndividuals = _numTopIndividuals;
        }



        /// <summary>
        /// Individuals are selected from this population in order to generate new individuals . 
        /// </summary>
        /// <param name="numTopIndividuals"></param>
        /// <returns></returns>
        public override void Selection(List<Individual> _Pt_List, List<Individual> _Pnot_List)
        {
            // clear top individuals list
           // m_topIndList.Clear();
            this.Individuals_List.Clear();
            int numTopIndCount = 0;
            // Select from Pt (Current archive)
            if (_Pt_List.Count != 0)
            {

                var topInd = from element in _Pt_List
                             orderby element.F descending
                             select element;

                numTopIndCount = (m_numTopIndividuals > topInd.Count())? topInd.Count(): m_numTopIndividuals;

                for (int i = 0; i < numTopIndCount; i++)
                {
                    //m_topIndList.Add(topInd.ElementAt(i));
                    this.Individuals_List.Add(topInd.ElementAt(i));
                    //_Pt_List.Remove(topInd.ElementAt(i));
                }
                _Pt_List.RemoveRange(0, numTopIndCount);
            }// end if
            
            // Select from P Not (external archive)
            if(_Pnot_List.Count != 0)
            {
                var topInd = from element in _Pnot_List
                             orderby element.F descending
                             select element;

                numTopIndCount = (m_numTopIndividuals > topInd.Count()) ? topInd.Count() : m_numTopIndividuals;
                for (int i = 0; i < numTopIndCount; i++)
                {
                   // m_topIndList.Add(topInd.ElementAt(i));
                    this.Individuals_List.Add(topInd.ElementAt(i));
                    //topInd.ElementAt(i).Print();
                   // _Pnot_List.Remove(topInd.ElementAt(i));
                }
                _Pnot_List.RemoveRange(0, numTopIndCount);
            }

            // update individual list in temporary population 
            // in order to crossover and mutation
           // this.Individuals_List = m_topIndList;
           
        }

       /// <summary>
       ///  two parents for crossover and mutation
       /// </summary>
        Individual parent1 = null;
        Individual parent2 = null;
        //bool parent1IsChangedAgg = false;
        //bool parent2IsChangedAgg = false;

       /// <summary>
       /// get two parents and make crossover in random position 
       /// </summary>
        public override void Crossover() 
        {
            //if (m_topIndList.Count < 2) throw new ArgumentException("You cannot make crossover. You should have at least two parents.");
            if (this.Individuals_List.Count < 2)
            {
                Individuals_List.Clear();
                return;
            }

            if (random.NextDouble() < m_crossoverFraction)
            {
                List<Individual> tmpList = new List<Individual>();
                tmpList = Individuals_List;
                //get two parents and make crossover in random position
                for (int i = 0; i < Individuals_List.Count/2; ++i)
                {
                    parent1 = getRandTopIndividual();
                    Individuals_List.Remove(parent1);
                    parent2 = getRandTopIndividual();
                    Individuals_List.Remove(parent2);
                    //Individuals_List.Clear();
                    //Individuals_List.Add(parent1);
                    //Individuals_List.Add(parent2);

                    //if (NeighbourPopulation.Individuals_List.Contains(parent1) == true)
                    //{
                    //    NeighbourPopulation.Individuals_List.Remove(parent1);
                    //}
                    //if (NeighbourPopulation.Individuals_List.Contains(parent2) == true)
                    //{
                    //    NeighbourPopulation.Individuals_List.Remove(parent2);
                    //}
                    //if (NeighbourPopulation2.Individuals_List.Contains(parent1) == true)
                    //{
                    //    NeighbourPopulation2.Individuals_List.Remove(parent1);
                    //}
                    //if (NeighbourPopulation2.Individuals_List.Contains(parent2) == true)
                    //{
                    //    NeighbourPopulation2.Individuals_List.Remove(parent2);
                    //}


                    // if(NeighbourPopulation.Individuals_List.Contains())

                    //generate random position for crossover from 1 to one step before end
                    int numAttemptsToFindCrossoverPosition = 10;
                    bool isSuccessfullCrossoverPosition = false;

                    int minSegSize = parent1.MinSegmentSize;
                    //int randPosition = (parent1.Gene.Length > minSegSize * 2)? 
                    //    random.Next(minSegSize, parent1.IndividualSize - minSegSize):
                    //    random.Next(1, parent1.IndividualSize - 1);
                    
                    int randPosition = random.Next(1, parent1.IndividualSize - 1);
                    //     int randPosition = 4;
                    parent1.IndividualSegmentList.Sort();
                    parent2.IndividualSegmentList.Sort();

                    do {
                        

                    int parent1SplitPosition = getSplitPosition(randPosition, parent1);
                    int parent2SplitPosition = getSplitPosition(randPosition, parent2);

                    if (parent1SplitPosition > 0 && parent2SplitPosition > 0)
                    {

                        // make crossover
                        makeChildren(randPosition, parent1, parent1SplitPosition, parent2, parent2SplitPosition);
                            isSuccessfullCrossoverPosition = true;
                    }

                        // -find another rand position and make childrens
                         else
                         {
                            isSuccessfullCrossoverPosition = false;
                            randPosition = random.Next(1, parent1.IndividualSize - 1);
                            //randPosition = (parent1.Gene.Length > minSegSize * 2)? 
                            //random.Next(minSegSize, parent1.IndividualSize - minSegSize):
                            //random.Next(1, parent1.IndividualSize - 1);

                            numAttemptsToFindCrossoverPosition--;
                         }
                    } while (numAttemptsToFindCrossoverPosition > 0 && !isSuccessfullCrossoverPosition);


                    Individuals_List.Add(parent1);
                    Individuals_List.Add(parent2);
                }
            }
            else {
             //   Individuals_List.Clear();
            }


            




        }




       /// <summary>
       /// 
       /// </summary>
       /// <param name="position"></param>
       /// <param name="parent1"></param>
       /// <param name="parent1SplitPosition"></param>
       /// <param name="parent2"></param>
       /// <param name="parent2SplitPosition"></param>
        private void makeChildren(int position,  Individual parent1, int parent1SplitPosition, 
                                                 Individual parent2, int parent2SplitPosition)
        {
            List< Segment> child1 = new List< Segment>();
            List< Segment> child2 = new List< Segment>();
            
           
#if TEST_PRINT_OPENED
            System.Console.WriteLine("Parent 1 Before Crossover: id: "+ parent1.IndividualID);
            parent1.Print();

            System.Console.WriteLine("Parent 2  Before Crossover: id: "+ parent2.IndividualID);
            parent2.Print();
#endif

            try
            {
                // copy all segments from parent 1 until split position of parent 1  
                for (int i = 0; i < parent1SplitPosition; i++)
                {
                    child1.Add(parent1.IndividualSegmentList.ElementAt(i));
                }

                // copy all segments from parent 2 until split position of parent 2
                for (int i = 0; i < parent2SplitPosition; i++)
                {
                    child2.Add(parent2.IndividualSegmentList.ElementAt(i));
                }

                // add from left of splitting in parent 1
                Segment segLeftBoundaryParent1 = parent1.IndividualSegmentList.ElementAt(parent1SplitPosition);
                if (position != segLeftBoundaryParent1.SegmentStartIndex)
                {
                    child1.Add(new Segment(segLeftBoundaryParent1.SegmentStartIndex, position, segLeftBoundaryParent1.MinSegmentSize));
                }

                // add from left of splitting in parent 2
                Segment segLeftBoundaryParent2 = parent2.IndividualSegmentList.ElementAt(parent2SplitPosition);
                if (position != segLeftBoundaryParent2.SegmentStartIndex)
                {
                    child2.Add(new Segment(segLeftBoundaryParent2.SegmentStartIndex, position, segLeftBoundaryParent2.MinSegmentSize));
                }


                /**********************  RIGHT SIDE  *****************************************************/
                // add from right of splitting from parent 2 to child 1
                Segment segRightBoundaryParent2 = parent2.IndividualSegmentList.ElementAt(parent2SplitPosition);
                child1.Add(new Segment(position, segRightBoundaryParent2.SegmentEndIndex, segRightBoundaryParent2.MinSegmentSize));


                // add from right of splitting from parent 1 to child 2
                Segment segRightBoundaryParent1 = parent1.IndividualSegmentList.ElementAt(parent1SplitPosition);
                child2.Add(new Segment(position, segRightBoundaryParent1.SegmentEndIndex, segRightBoundaryParent1.MinSegmentSize));



                for (int i = parent2SplitPosition + 1; i < parent2.IndividualSegmentList.Count; i++)
                {
                    child1.Add(parent2.IndividualSegmentList.ElementAt(i));
                }

                for (int i = parent1SplitPosition + 1; i < parent1.IndividualSegmentList.Count; i++)
                {
                    child2.Add(parent1.IndividualSegmentList.ElementAt(i));
                }


                parent1.IndividualSegmentList.Clear();
                parent2.IndividualSegmentList.Clear();

                parent1.IndividualSegmentList = child1;
                parent2.IndividualSegmentList = child2;


                parent1.calculateAggregation();
                parent2.calculateAggregation();


            }
            catch (Exception)
            {

                Console.WriteLine("make children error");
            }

            #if TEST_PRINT_OPENED
            System.Console.WriteLine("........After Crossover at position : " + position);
            System.Console.WriteLine("........After Crossover Parent 1: id: "+ parent1.IndividualID);
            parent1.Print();

            System.Console.WriteLine("........After Crossover Parent 2:"+ parent2.IndividualID);
            parent2.Print();
            #endif

            
        }

       /// <summary>
       /// To check is it legal to make crossover.
       /// </summary>
       /// <param name="position"></param>
       /// <param name="parent"></param>
       /// <returns></returns>
        private int getSplitPosition(int position, Individual parent)
        {

            // check if single point in parent 1 has min segment size with left and right boundary 
            for (int i = 0; i < parent.IndividualSegmentList.Count; i++)
            {
                Segment parentSegment = parent.IndividualSegmentList.ElementAt(i);

                // if position is already on boudary
                if (parentSegment.SegmentStartIndex == position /*||
                   parentSegment.SegmentEndIndex == position*/)
                    return i;

                // if position between two boundaries
                if (parentSegment.SegmentStartIndex < position &&
                    parentSegment.SegmentEndIndex > position)
                {
                    // found suitable boundary, check if min segment size is occurs
                    if (position - parentSegment.SegmentStartIndex >= parentSegment.MinSegmentSize &&
                        parentSegment.SegmentEndIndex - position >= parentSegment.MinSegmentSize)
                            return i;
                }
            }//for

            return -1;
        }










        /// <summary>
        ///  mutation of temporary population
        ///  mutation that shifts a boundary of the individual
        /// </summary>
        public override void Mutate() 
        {
            if (random.NextDouble() < m_mutationFraction)
            {


                if (parent1 != null && parent2 != null)
                {
                    
                    int parent1MutBoundInd = random.Next(parent1.IndividualSegmentList.Count);
                    int parent2MutBoundInd = random.Next(parent2.IndividualSegmentList.Count);
                    int numAttemptsToSuccessfullMutation = 10;

                    while (doMutation(parent1, parent1MutBoundInd) == false &&
                        numAttemptsToSuccessfullMutation > 0)
                    {
                        numAttemptsToSuccessfullMutation--;
                    }

                    numAttemptsToSuccessfullMutation = 10;

                    while (doMutation(parent2, parent2MutBoundInd) == false &&
                        numAttemptsToSuccessfullMutation > 0)
                    {
                        numAttemptsToSuccessfullMutation--;
                    }
                    
                }
            }
           


        }

        private bool doMutation(Individual parent, int parentMutBoundInd)
        {

            parent.IndividualSegmentList.Sort();

            if (parentMutBoundInd > 0 && doLeftMutation(parent, parentMutBoundInd - 1) == true)
            {
                parent.calculateAggregation();
                return true;
            }

            else if (parentMutBoundInd < parent.IndividualSegmentList.Count-1 &&
                     doRightMutation(parent, parentMutBoundInd) == true)
                {
                    parent.calculateAggregation();
                    return true;
                }
            
            return false;

        }

       /// <summary>
       /// 
       /// </summary>
       /// <param name="parent"></param>
       /// <param name="parentMutBoundInd"></param>
       /// <returns></returns>
        private bool doRightMutation(Individual parent, int parentMutBoundInd)
        {
           // if (parentMutBoundInd < 0) return false;

            

                Segment parentMutBoundSegment = parent.IndividualSegmentList.ElementAt(parentMutBoundInd);
                int numNuclInCodon = Codon.CodonNumNucleotides;

                //if we can move boundary to right  =  three nucleotides
                if ((parentMutBoundSegment.SegmentEndIndex + 1) - parentMutBoundSegment.SegmentStartIndex >=
                    parentMutBoundSegment.MinSegmentSize)
                {
                    // next segment is bigger then min seg size
                    Segment parentMutRightBoundSegment = parent.IndividualSegmentList.ElementAt(parentMutBoundInd + 1);
                    if (parentMutRightBoundSegment.SegmentEndIndex - (parentMutRightBoundSegment.SegmentStartIndex + 1) >=
                        parentMutRightBoundSegment.MinSegmentSize)
                    {
#if TEST_PRINT_OPENED
                        Console.WriteLine("Before right Mutation parent id: " + parent.IndividualID);
                        parent.Print();
#endif
                        parentMutBoundSegment.SegmentEndIndex += 1;
                        parentMutBoundSegment.IsChanged = true;
                        parentMutBoundSegment.SegmentSize = parentMutBoundSegment.SegmentEndIndex - parentMutBoundSegment.SegmentStartIndex;

                        parentMutRightBoundSegment.SegmentStartIndex += 1;
                        parentMutRightBoundSegment.IsChanged = true;
                        parentMutRightBoundSegment.SegmentSize = parentMutRightBoundSegment.SegmentEndIndex - parentMutRightBoundSegment.SegmentStartIndex;
#if TEST_PRINT_OPENED
                        Console.WriteLine("After right  Mutation parent id: " + parent.IndividualID);
                        parent.Print();
#endif
                        return true;
                    }


                }

            
            return false;
        }

       /// <summary>
       /// 
       /// </summary>
       /// <param name="parent"></param>
       /// <param name="parentMutBoundInd"></param>
       /// <returns></returns>
        private bool doLeftMutation(Individual parent, int parentMutBoundInd)
        {
            if(parentMutBoundInd < 0) return false;

    
           Segment parentMutBoundSegment = parent.IndividualSegmentList.ElementAt(parentMutBoundInd);
           int numNuclInCodon = Codon.CodonNumNucleotides;

           if ((parentMutBoundSegment.SegmentEndIndex - 1) - parentMutBoundSegment.SegmentStartIndex >=
                parentMutBoundSegment.MinSegmentSize)
            {
#if TEST_PRINT_OPENED
                Console.WriteLine("Before left Mutation parent id: " + parent.IndividualID);
                parent.Print();
#endif
                parentMutBoundSegment.SegmentEndIndex -= 1;
                parentMutBoundSegment.IsChanged = true;
                parentMutBoundSegment.SegmentSize = parentMutBoundSegment.SegmentEndIndex - parentMutBoundSegment.SegmentStartIndex;

                Segment parentMutRightBoundSegment = parent.IndividualSegmentList.ElementAt(parentMutBoundInd+1);
                parentMutRightBoundSegment.SegmentStartIndex -= 1;
                parentMutRightBoundSegment.IsChanged = true;
                parentMutRightBoundSegment.SegmentSize = parentMutRightBoundSegment.SegmentEndIndex - parentMutRightBoundSegment.SegmentStartIndex;

  #if TEST_PRINT_OPENED             
                Console.WriteLine("After left Mutation parent id: " + parent.IndividualID);
                parent.Print();
#endif
                return true;
            }

           return false;
        }// end mutation

             Random random = new Random();
            private   Individual  getRandTopIndividual()
            {
                calculateProbabilityOfSelection();
                var topInd = from element in Individuals_List
                             orderby element.ProbabilityOfSelection descending
                             select element;

                return topInd.ElementAt(0);
         //      return this.Individuals_List.ElementAt( random.Next(this.Individuals_List.Count) );
               
            }


    }
}


